home *** CD-ROM | disk | FTP | other *** search
- /* SMTP Server state machine - see RFC 821
- * Very simple implementation; no forwarding allowed
- * (who wants to re-create "sendmail" ??)
- */
- #include <stdio.h>
- #include "machdep.h"
- #include "mbuf.h"
- #include "netuser.h"
- #include "timer.h"
- #include "tcp.h"
- #include "smtp.h"
- #ifdef LATTICE
- #define tmpfile(x) tmpnam("testingxxxxxxxxxx")
- #endif
-
- /* Command table */
- static char *commands[] = {
- "helo",
- #define HELO_CMD 0
- "noop",
- #define NOOP_CMD 1
- "mail from:",
- #define MAIL_CMD 2
- "quit",
- #define QUIT_CMD 3
- "rcpt to:",
- #define RCPT_CMD 4
- "help",
- #define HELP_CMD 5
- "data",
- #define DATA_CMD 6
- "rset",
- #define RSET_CMD 7
- NULLCHAR
- };
-
- /* Reply messages */
- static char help[] = "214-Commands:\r\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET\r\n214 End\r\n";
- static char banner[] = "220 %s SMTP Ready\r\n";
- static char closing[] = "221 Closing\r\n";
- static char ok[] = "250 Ok\r\n";
- static char reset[] = "250 Reset state\r\n";
- static char sent[] = "250 Sent\r\n";
- static char ourname[] = "250 %s, Share and Enjoy!\r\n";
- static char enter[] = "354 Enter mail, end with .\r\n";
- static char ioerr[] = "452 Temp file write error\r\n";
- static char mboxerr[] = "452 Mailbox %s write error\r\n";
- static char badcmd[] = "500 Command unrecognized\r\n";
- static char syntax[] = "501 Syntax error\r\n";
- static char needrcpt[] = "503 Need RCPT (recipient)\r\n";
- static char badname[] = "550 Can't open mailbox for %s\r\n";
- static struct tcb *smtp_tcb;
- /* Start up SMTP receiver service */
- smtp_start(argc,argv)
- int argc;
- char *argv[];
- {
- struct socket lsocket;
- void r_mail(),s_mail();
-
- lsocket.address = ip_addr;
- if(argc < 2)
- lsocket.port = SMTP_PORT;
- else
- lsocket.port = atoi(argv[1]);
-
- smtp_tcb = open_tcp(&lsocket,NULLSOCK,
- TCP_PASSIVE,0,r_mail,NULLVFP,s_mail,0,(int *)NULL);
- }
-
- /* Shutdown SMTP service (existing connections are allowed to finish) */
- smtp_stop()
- {
- if(smtp_tcb != NULLTCB)
- close_tcp(smtp_tcb);
- }
- /* SMTP connection state change upcall handler */
- static void
- s_mail(tcb,old,new)
- struct tcb *tcb;
- char old,new;
- {
- struct mail *mp,*mail_create();
-
- switch(new){
- #ifdef QUICKSTART
- case SYN_RECEIVED:
- #else
- case ESTABLISHED:
- #endif
- if((mp = mail_create(tcb)) == NULLMAIL){
- close_tcp(tcb);
- break;
- }
- tprintf(mp->tcb,banner,hostname);
- log(tcb,"open SMTP");
- break;
- case CLOSE_WAIT:
- mp = (struct mail *)tcb->user;
- mail_delete(mp);
- close_tcp(tcb);
- break;
- case CLOSED:
- log(tcb,"close SMTP");
- del_tcp(tcb);
- /* Check if server is being shut down */
- if(tcb == smtp_tcb)
- smtp_tcb = NULLTCB;
- break;
- }
- }
-
- /* SMTP receiver upcall handler */
- static void
- r_mail(tcb,cnt)
- struct tcb *tcb;
- int16 cnt;
- {
- register struct mail *mp;
- char *index(),*inet_ntoa(),c;
- struct mbuf *bp;
- void docommand(),deliver(),doline();
-
- if((mp = (struct mail *)tcb->user) == NULLMAIL){
- /* Unknown session */
- close_tcp(tcb);
- return;
- }
- recv_tcp(tcb,&bp,cnt);
- /* Assemble an input line in the session buffer.
- * Return if incomplete
- */
- while(pullup(&bp,&c,1) == 1){
- switch(c){
- case '\r': /* Strip cr's */
- continue;
- case '\n': /* Complete line; process it */
- mp->buf[mp->cnt] = '\0';
- doline(mp);
- break;
- default: /* Assemble line */
- mp->buf[mp->cnt++] = c;
- break;
- }
- }
- }
- /* Process a line read on an SMTP connection (any state) */
- static
- void
- doline(mp)
- register struct mail *mp;
- {
- switch(mp->state){
- case COMMAND_STATE:
- docommand(mp);
- break;
- case DATA_STATE:
- tcp_output(mp->tcb); /* Send ACK; disk I/O is slow */
- if(mp->buf[0] == '.' && strlen(mp->buf) == 1){
- fprintf(mp->data,"\n"); /* Leave a blank line between msgs */
-
- mp->state = COMMAND_STATE;
- deliver(mp); /* Also sends appropriate response */
- break;
- }
- /* Append to data file */
- if(fprintf(mp->data,"%s\n",mp->buf) < 0){
- mp->state = COMMAND_STATE;
- tprintf(mp->tcb,ioerr);
- }
- break;
- }
- mp->cnt = 0;
- }
- /* Create control block, initialize */
- static struct mail *
- mail_create(tcb)
- register struct tcb *tcb;
- {
- register struct mail *mp;
- char *calloc();
-
- if((mp = (struct mail *)calloc(1,sizeof (struct mail))) == NULLMAIL){
- return NULLMAIL;
- }
- mp->tcb = tcb; /* Downward pointer */
- tcb->user = (int *)mp; /* Upward pointer */
- return mp;
- }
-
- /* Free resources, delete control block */
- static
- mail_delete(mp)
- register struct mail *mp;
- {
- register struct addr *ap,*ap1;
-
- if(mp->system != NULLCHAR)
- free(mp->system);
- for(ap = mp->to;ap != NULLADDR;ap = ap1){
- if(ap->val != NULLCHAR)
- free(ap->val);
- ap1 = ap->next;
- free((char *)ap);
- }
- if(mp->data != NULLFILE)
- fclose(mp->data);
- free((char *)mp);
- }
-
- /* Parse and execute mail commands */
- static
- void
- docommand(mp)
- register struct mail *mp;
- {
- char mailbox[50];
- char *cmd,*arg,*cp,*cp1,**cmdp;
- char *index(),*malloc(),*getname();
- struct tcb *tcb;
- struct addr *ap;
- #ifdef LATTICE
- FILE *fp;
- #else
- FILE *tmpfile(),*fp;
- #endif
- #ifdef DATE
- long t;
- char *ctime();
- #endif
-
- cmd = mp->buf;
- if(mp->cnt < 4){
- /* Can't be a legal SMTP command */
- tprintf(mp->tcb,badcmd);
- return;
- }
- cmd = mp->buf;
-
- /* Translate entire buffer to lower case */
- for(cp = cmd;*cp != '\0';cp++)
- *cp = tolower(*cp);
-
- /* Find command in table; if not present, return syntax error */
- for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
- if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
- break;
- if(*cmdp == NULLCHAR){
- tprintf(mp->tcb,badcmd);
- return;
- }
- arg = &cmd[strlen(*cmdp)];
- /* Skip spaces after command */
- while(*arg == ' ')
- *arg++;
- /* Execute specific command */
- switch(cmdp-commands){
- case HELO_CMD:
- if((mp->system = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
- /* If the system is out of memory, just close */
- close_tcp(mp->tcb);
- mail_delete(mp);
- break;
- } else {
- strcpy(mp->system,arg);
- tprintf(mp->tcb,ourname,hostname);
- }
- break;
- case NOOP_CMD:
- case MAIL_CMD: /* Rather useless */
- tprintf(mp->tcb,ok);
- break;
- case QUIT_CMD:
- tprintf(mp->tcb,closing);
- close_tcp(mp->tcb);
- mail_delete(mp);
- break;
- case RCPT_CMD: /* Specify recipient */
- if((cp = getname(arg)) == NULLCHAR){
- tprintf(mp->tcb,syntax);
- break;
- }
- /* Strip the @host part; all names must be local */
- if((cp1 = index(cp,'@')) != NULLCHAR)
- *cp1 = '\0';
-
- /* Check to see if we can open the mailbox */
- sprintf(mailbox,MAILSPOOL,cp);
- #ifdef AMIGA
- /* probably could use AMIGA's Lock()/Unlock(), but that
- seems like overkill */
- if((fp = fopen(mailbox,"r")) == NULL){
- #else
- if((fp = fopen(mailbox,"a+")) == NULL){
- #endif
- tprintf(mp->tcb,badname,cp);
- break;
- }
- fclose(fp);
- /* Allocate an entry on the recipient list. This
- * assembles the list backwards, but what the heck.
- */
- if((ap = (struct addr *)malloc(sizeof(struct addr))) == NULLADDR){
- close_tcp(mp->tcb);
- mail_delete(mp);
- break;
- }
- if((ap->val = malloc((unsigned)strlen(mailbox)+1)) == NULLCHAR){
- free((char *)ap);
- close_tcp(mp->tcb);
- mail_delete(mp);
- break;
- }
- strcpy(ap->val,mailbox);
- ap->next = mp->to;
- mp->to = ap;
- tprintf(mp->tcb,ok);
- break;
- case HELP_CMD:
- tprintf(mp->tcb,help);
- break;
- case DATA_CMD:
- if(mp->to == NULLADDR){
- tprintf(mp->tcb,needrcpt);
- break;
- }
- tcp_output(mp->tcb); /* Send ACK; disk I/O is slow */
- if((mp->data = tmpfile()) == NULLFILE){
- tprintf(mp->tcb,ioerr);
- break;
- }
- #ifdef DATE
- /* Add timestamp; ctime adds newline */
- time(&t);
- if(mp->system != NULLCHAR)
- fprintf(mp->data,
- "Received: from %s by %s\n\twith SMTP ; %s",
- mp->system,
- hostname,
- ctime(&t));
- else
- fprintf(mp->data,"Received: %s\n",ctime(&t));
- #endif
- if(ferror(mp->data)){
- tprintf(mp->tcb,ioerr);
- } else {
- mp->state = DATA_STATE;
- tprintf(mp->tcb,enter);
- }
- break;
- case RSET_CMD:
- tcb = mp->tcb;
- mail_delete(mp);
- if((mp = mail_create(tcb)) == NULLMAIL){
- close_tcp(tcb);
- } else {
- mp->state = COMMAND_STATE;
- tprintf(mp->tcb,reset);
- }
- break;
- }
- }
- /* Given a string of the form <user@host>, extract the part inside the
- * brackets and return a pointer to it.
- */
- static
- char *
- getname(cp)
- register char *cp;
- {
- char *cp1;
- char *index(),*strncpy();
-
- if((cp = index(cp,'<')) == NULLCHAR){
- return NULLCHAR;
- }
- cp++; /* cp -> first char of name */
- if((cp1 = index(cp,'>')) == NULLCHAR){
- return NULLCHAR;
- }
- *cp1 = '\0';
- return cp;
- }
- /* Deliver mail to the appropriate mail boxes and delete temp file */
- static
- void
- deliver(mp)
- register struct mail *mp;
- {
- char *index();
- int c;
- register struct addr *ap;
- register FILE *fp;
-
- for(ap = mp->to;ap != NULLADDR;ap = ap->next){
- fseek(mp->data,0L,0); /* rewind */
- fp = fopen(ap->val,"a+");
- while((c = getc(mp->data)) != EOF){
- if(putc(c,fp) == EOF)
- break;
- }
- if(ferror(fp)){
- fclose(fp);
- tprintf(mp->tcb,mboxerr,ap->val);
- fclose(mp->data);
- mp->data = NULLFILE;
- return;
- }
- fclose(fp);
- }
- tprintf(mp->tcb,sent);
- fclose(mp->data);
- mp->data = NULLFILE;
- }
-